﻿using System;
using System.Collections.Generic;
using ObjectSync.Interfaces;
using ObjectSync.Util;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Reflection;
using Lidgren.Network;


namespace ObjectSync
{
    [Serializable]
    public class RemoteObject: IRemote
    {

        private string executionAssembly = "";
        private string executionNameSpace = "";
		//private NetOutgoingMessage outMessageBuffer;

        public string ExecutionAssembly
        {
            get
            {
                return executionAssembly;
            }
            set
            {
                executionAssembly = value;
            }
        }

        public string ExecutionNameSpace
        {
            get
            {
                return executionNameSpace;
            }
            set
            {
                executionNameSpace = value;
            }
        }





        /// <summary>
        /// Do the remote method execution and return the results
        /// </summary>
        /// <param name="methodName">The name of the method to be executed</param>
        /// <param name="parameters">The list of parameters needed</param>
        /// <returns>The list of objects resulting of the execution</returns>
		public object ExecuteMethod(string assemblyName, string objectName, string methodName, object[] parameters)
        {
            NetBuffer outMessageBuffer = new NetBuffer();

            outMessageBuffer.Write((UInt16)MessageType.Exec);

            Stream str;
            Byte[] outBuff;
            
            string asm;
            string oname; 

            if (executionAssembly != "") asm = executionAssembly;
            else asm = assemblyName;

            if (executionNameSpace != "") oname = executionNameSpace + "." + objectName;
            else oname = objectName;

            //outMessageBuffer.Write(asm.Length);
            outMessageBuffer.Write(asm);

            //outMessageBuffer.Write(oname.Length);
            outMessageBuffer.Write(oname);

            //outMessageBuffer.Write(methodName.Length);
            outMessageBuffer.Write(methodName);

            BinaryFormatter formatter = new BinaryFormatter();

            outMessageBuffer.Write(parameters.Length);

            foreach (object par in parameters)
            {

                str = new MemoryStream();
                formatter.Serialize(str, par);
                str.Position = 0;

                outBuff = new Byte[str.Length];

                str.Read(outBuff, 0, (int)str.Length);

                outMessageBuffer.Write(outBuff.Length);
                outMessageBuffer.Write(outBuff);

            }

            long id = RemoteManager.NextRemoteID;
            outMessageBuffer.Write(id);

            NetworkManager.Instance.Communicator.Send(outMessageBuffer);

            int i = 0;
            object resp = null;
            while (i < ConfigManager.Instance.RemoteCallTimeout) 
            {
                resp = RemoteManager.getAnswer(id);

                if (resp == null)
                {
                    i = ConfigManager.Instance.RemoteCallTimeout + 5;
                    break;
                }
                if (resp.GetType() != typeof(NoAnswer))
                {
                    i = ConfigManager.Instance.RemoteCallTimeout + 5;
                    break;
                }

                //resp = null;
                Thread.Sleep(5);
                i++;
            }

            if (i == ConfigManager.Instance.RemoteCallTimeout) throw new Exception("Timeout in remoteCall");

            return resp;

 
        }

    }
}
